<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>不同的打包工具的环境变量 | TBlog</title>
    <meta name="description" content="TBlog with vitePress">
    <link rel="stylesheet" href="/interview/assets/style.9156a4ed.css">
    <link rel="modulepreload" href="/interview/assets/Home.b86443e2.js">
    <link rel="modulepreload" href="/interview/assets/app.49accf37.js">
    <link rel="modulepreload" href="/interview/assets/Pit_index.md.603d46aa.lean.js">
    <link rel="modulepreload" href="/interview/assets/app.49accf37.js">
    <link rel="icon" href="/favicon.ico">
    <meta name="twitter:title" content="不同的打包工具的环境变量 | TBlog">
    <meta property="og:title" content="不同的打包工具的环境变量 | TBlog">
  </head>
  <body>
    <div id="app"><!--[--><div class="theme"><header class="nav-bar" data-v-7161a24b><div class="sidebar-button" data-v-7161a24b><svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class></path></svg></div><a class="nav-bar-title" href="/interview/" aria-label="TBlog, back to home" data-v-7161a24b data-v-4a583abe><!----> TBlog</a><div class="flex-grow" data-v-7161a24b></div><div class="nav" data-v-7161a24b><nav class="nav-links" data-v-7161a24b data-v-15acbf05><!--[--><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:66" target="_blank" rel="noopener noreferrer" data-v-641633f9>Music <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:80" target="_blank" rel="noopener noreferrer" data-v-641633f9>CMC <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://juejin.cn/user/2163479676978734/posts" target="_blank" rel="noopener noreferrer" data-v-641633f9>掘金 <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://gitee.com/tu_zhiwei98" target="_blank" rel="noopener noreferrer" data-v-641633f9>Gitee <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><!--]--><!----><!----></nav></div><!--[--><!--]--></header><aside class="sidebar" data-v-6b49cdcd><nav class="nav-links nav" data-v-6b49cdcd data-v-15acbf05><!--[--><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:66" target="_blank" rel="noopener noreferrer" data-v-641633f9>Music <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="http://101.43.7.188:80" target="_blank" rel="noopener noreferrer" data-v-641633f9>CMC <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://juejin.cn/user/2163479676978734/posts" target="_blank" rel="noopener noreferrer" data-v-641633f9>掘金 <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><div class="item" data-v-15acbf05><div class="nav-link" data-v-15acbf05 data-v-641633f9><a class="item isExternal" href="https://gitee.com/tu_zhiwei98" target="_blank" rel="noopener noreferrer" data-v-641633f9>Gitee <svg class="icon outbound" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" data-v-641633f9><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div></div><!--]--><!----><!----></nav><!--[--><!--]--><ul class="sidebar-links" data-v-6b49cdcd><!--[--><li class="sidebar-link"><p class="sidebar-link-item">Vue3.2X</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/">基本语法&amp;使用</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/VueX">VueX4.x</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/Router">Router4.x</a><!----></li><li class="sidebar-link"><p class="sidebar-link-item">部分源码解析</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/reactive">响应式系统</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/source/Watch">Watch</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vue/source/nextTick">nextTick</a><!----></li></ul></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">WebPack</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/">基础配置</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/optimize">优化方案</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/common">常见的Loader以及Plugin</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/custom">自定义Loader以及Plugin</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/WebPack/advanced">手写简单的MY_Webpack</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Javascript</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Javascript/">基本语法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Javascript/advanced">高级语法</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">TypeScript</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/">基本语法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/combat">业务实战</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/TypeScript/advanced">高阶使用</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">浏览器相关</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/">基本原理</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/plugin">浏览器插件</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Browser/advanced">深入原理</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">计算机网络</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Network/">计算机网络</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Network/security">前端安全</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">HTML/CSS</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/CSS/">CSS</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/HTML/">HTML</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Vite2.X</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vite/">基础配置</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Vite/optimize">原理以及优化</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">Node</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Node/">基本知识</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Node/pit">踩坑记录</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">其他前端工具</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Git/">Git</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Jenkins/">Jenkins</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/npm/">包管理工具</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">踩坑记录以及经验</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item active" href="/interview/Pit/">踩坑</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#不同的打包工具的环境变量">不同的打包工具的环境变量</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#webpack">webpack</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#vite">vite</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#功能特点">功能特点</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#支持多种请求方式">支持多种请求方式:</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#axios-的实例和拦截器">axios 的实例和拦截器</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#axios-的配置选项">Axios 的配置选项</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#特性">特性</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#代码质量">代码质量</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#editorconfig">editorConfig</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#eslint">ESLint</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#husky-lint-staged-prettier">husky + lint-staged + prettier</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#commitlint">commitlint</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#项目目录结构">项目目录结构</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#axios-源码编写">axios 源码编写</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#axios-基础框架">axios 基础框架</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#_2、createinstance-方法">2、createInstance 方法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#_6、支持-axios-request-与-axios-get-axios-post-axios-put-等形式调用">6、支持 axios.request 与 axios.get | axios.post | axios.put 等形式调用</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#发送真实请求">发送真实请求</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#目录结构调整">目录结构调整</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#适配器">适配器</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#完善-axios-类方法">完善 Axios 类方法</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#拦截器">拦截器</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#默认配置">默认配置</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#取消功能">取消功能</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#withcredentials">withCredentials</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#csrf-防御">CSRF 防御</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#总结">总结</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#宝藏网站">宝藏网站</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/solution/">一些解决方案</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">面经</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Interview/">xxx面试</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Interview/two">xxX面试</a><!----></li></ul></li><li class="sidebar-link"><p class="sidebar-link-item">个人学习历程</p><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/">个人情况说明</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/pre">入行工作前</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="/interview/Myself/one">工作第一年</a><!----></li></ul></li><!--]--></ul><!--[--><!--]--></aside><!-- TODO: make this button accessible --><div class="sidebar-mask"></div><main class="page" data-v-7eddb2c4><div class="container" data-v-7eddb2c4><!--[--><!--]--><div style="position:relative;" class="content" data-v-7eddb2c4><div><h2 id="不同的打包工具的环境变量"><a class="header-anchor" href="#不同的打包工具的环境变量" aria-hidden="true">#</a> 不同的打包工具的环境变量</h2><h3 id="webpack"><a class="header-anchor" href="#webpack" aria-hidden="true">#</a> webpack</h3><p>主要通过 defineplugin</p><p>process.<wbr>env.NODE_ENV 来判断 生产环境</p><h3 id="vite"><a class="header-anchor" href="#vite" aria-hidden="true">#</a> vite</h3><p>环境变量的设置</p><ul><li><strong>注意：</strong> 命名规范 ``VITE_<code>为前缀的变量才会暴露给经过</code>vite`处理的代码</li><li><code>vite serve</code> 时是<code>development</code>开发模式， <code>vite build</code>时是<code>production</code>生产模式。</li><li>分别创建配置文件： <code>.env.development</code> 、<code>.env.production</code></li></ul><p>环境变量的读取</p><div class="language-javascript"><pre><code><span class="token keyword">import</span><span class="token punctuation">.</span>meta<span class="token punctuation">.</span>env <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">通过这个可以拿到相应的参数</span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">{</span><span class="token constant">BASE_URL</span><span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span> <span class="token constant">MODE</span><span class="token operator">:</span> <span class="token string">&quot;development&quot;</span><span class="token punctuation">,</span> <span class="token constant">DEV</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token constant">PROD</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token constant">SSR</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">}</span>
<span class="token constant">BASE_URL</span><span class="token operator">:</span> <span class="token string">&quot;/&quot;</span>
<span class="token constant">DEV</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token constant">MODE</span><span class="token operator">:</span> <span class="token string">&quot;development&quot;</span>
<span class="token constant">PROD</span><span class="token operator">:</span> <span class="token boolean">false</span>
<span class="token constant">SSR</span><span class="token operator">:</span> <span class="token boolean">false</span>

</code></pre></div><p>axios</p><p>Axios</p><h2 id="功能特点"><a class="header-anchor" href="#功能特点" aria-hidden="true">#</a> 功能特点</h2><ul><li>在浏览器中发送 XMLHttpRequests 请求</li><li>在 node.js 中发送 http 请求</li><li>支持 Promise API</li><li>拦截请求和响应</li><li>转换请求和响应数据</li></ul><h3 id="支持多种请求方式"><a class="header-anchor" href="#支持多种请求方式" aria-hidden="true">#</a> 支持多种请求方式:</h3><ul><li>axios(config) axios.request(config)</li><li>axios.get(url[, config]) axios.delete(url[, config])</li><li>axios.head(url[, config]) axios.post(url[, data[, config]])</li><li>axios.put(url[, data[, config axios.patch(url[, data[, config]])</li></ul><p>有时候, 我们可能需求同时发送两个请求</p><ul><li>使用 axios.all, 可以放入多个请求的数组.</li><li>axios.all([]) 返回的结果是一个数组，使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2</li></ul><h3 id="axios-的实例和拦截器"><a class="header-anchor" href="#axios-的实例和拦截器" aria-hidden="true">#</a> <strong>axios 的实例和拦截器</strong></h3><p>为什么要创建 axios 的实例呢?</p><ul><li>当我们从 axios 模块中导入对象时, 使用的实例是默认的实例.</li><li>当给该实例设置一些默认配置时, 这些配置就被固定下来了.</li><li>但是后续开发中, 某些配置可能会不太一样.</li><li>比如某些请求需要使用特定的 baseURL 或者 timeout 或者 content-Type 等.</li></ul><p>这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.</p><p>axios 的也可以设置拦截器：拦截每次请求和响应</p><ol><li>paxios.interceptors.request.use(请求成功拦截, 请求失败拦截)</li><li>paxios.interceptors.response.use(响应成功拦截, 响应失败拦截)</li></ol><p>具体案例</p><div class="language-typescript"><pre><code>axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&#39;http://123.207.32.32:8000/home/multidata&#39;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// 当我们拿到数据内部会调用resolve()方法 ,res的类型 是Axiosresponse&lt;T&gt; 类型  会自动推导</span>
<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">AxiosResponse<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token operator">=</span> <span class="token builtin">any</span><span class="token operator">&gt;</span></span>  <span class="token punctuation">{</span>
  data<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">;</span>
  status<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
  statusText<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  headers<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
  config<span class="token operator">:</span> AxiosRequestConfig<span class="token punctuation">;</span>
  request<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">可以使用 httpbin.rog</span><span class="token template-punctuation string">`</span></span> 来模拟我们的请求
</code></pre></div><h3 id="axios-的配置选项"><a class="header-anchor" href="#axios-的配置选项" aria-hidden="true">#</a> Axios 的配置选项</h3><div class="language-typescript"><pre><code><span class="token comment">// axios.defaults.baseURL = &#39;http:// xxxx.xxx&#39;        配置默认 url</span>
<span class="token comment">//  axios.default.timeout = 10000                    配置超时时间</span>
<span class="token comment">// axios.default.header = {}                       配置请求头</span>
<span class="token comment">//  withCredentials: false               跨域是否带Token</span>
<span class="token comment">//  transformRequest/Response: [function(data){}],    请求前/后的数据处理</span>
</code></pre></div><p>这些都是针对全局的 配置选项 也可以对每个请求进行单独的配置</p><div class="language-"><pre><code>axios.interceptors.request.use(
  (config) =&gt; {
    //  做一些想做的操作
    //  1. 给请求添加token
    //  2. 添加 isloading动画效果
    return config
  },
  (err) =&gt; {
    console.log(&#39;请求发送错误：&#39;)
  }
)
</code></pre></div><p>Axios 库的封装提取</p><p>为什么要进行这些库的抽取封装?</p><p>因为不抽取,代码之间的耦合度太高了 如果将来需要更改这些库 只要修改一个文件就可以了</p><p>可以抽取公共代码逻辑,代码更加优雅</p><p>对每个个实例进行 封装&#39;</p><h1 id="手写-axios-库并发布至-npm-线上完整过程"><a class="header-anchor" href="#手写-axios-库并发布至-npm-线上完整过程" aria-hidden="true">#</a> 手写 axios 库并发布至 npm 线上完整过程</h1><h1 id="前言"><a class="header-anchor" href="#前言" aria-hidden="true">#</a> 前言</h1><p><code>axios</code> 是一个非常小巧的 <code>HTTP</code> 库，但是其中蕴含了许多值得学习的编程思想。今天就让我们一起来学透它。</p><p>本文将分为以下部分循序渐进地学习 <code>axios</code> 库：</p><ol><li><code>axios</code> 简介</li><li><code>axios</code> 工程搭建</li><li><code>axios</code> 源码编写</li><li><code>axios</code> 打包发布</li></ol><p>如学习本文对您有所帮助，请点个赞把~ ~ 。</p><h1 id="axios-简介"><a class="header-anchor" href="#axios-简介" aria-hidden="true">#</a> axios 简介</h1><p><code>axios</code> 是一个基于 <code>promise</code> 的 <code>HTTP</code> 库，可以用在浏览器和 <code>node.js</code> 中。</p><h2 id="特性"><a class="header-anchor" href="#特性" aria-hidden="true">#</a> 特性</h2><ul><li>从浏览器中创建 <code>XMLHttpRequests</code> ，从 <code>node.js</code> 创建 <code>http</code> 请求；</li><li>支持 <code>Promise API</code> ；</li><li>拦截请求和响应；</li><li>转换请求数据和响应数据；</li><li>取消请求；</li><li>自动转换 <code>JSON</code> 数据；</li><li>客户端支持防御 <code>XSRF</code> 。</li></ul><h1 id="axios-工程搭建"><a class="header-anchor" href="#axios-工程搭建" aria-hidden="true">#</a> axios 工程搭建</h1><p>下面开始搭建自己的项目工程。</p><p>采用 <code>ES6+</code> 语法以及 <code>webpack</code> 打包。</p><p>初始化项目：</p><div class="language-javascript"><pre><code><span class="token number">1</span>、git 上面创建项目 lion<span class="token operator">-</span>axios
<span class="token number">2</span>、本地 git clone https<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>github<span class="token punctuation">.</span>com<span class="token operator">/</span>shiyou00<span class="token operator">/</span>lion<span class="token operator">-</span>axios<span class="token punctuation">.</span>git
<span class="token number">3</span>、进入 cd lion<span class="token operator">-</span>axios<span class="token operator">/</span>
<span class="token number">4</span>、初始化项目 npm init <span class="token operator">-</span>y
复制代码
</code></pre></div><p>配置 <code>webpack</code>：</p><p>开发环境配置文件： <code>webpack.dev.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">const</span> HtmlWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;html-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  mode<span class="token operator">:</span> <span class="token string">&quot;development&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 开发环境</span>
  devtool<span class="token operator">:</span> <span class="token string">&quot;cheap-module-eval-source-map&quot;</span><span class="token punctuation">,</span> <span class="token comment">// sourceMap用于错误调试</span>
  devServer<span class="token operator">:</span> <span class="token punctuation">{</span>
    contentBase<span class="token operator">:</span> <span class="token string">&quot;./example&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 服务器启动根目录设置为example</span>
    open<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 自动打开浏览器</span>
    port<span class="token operator">:</span> <span class="token number">8088</span><span class="token punctuation">,</span> <span class="token comment">// 端口号</span>
    hot<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 开启热更新，同时要配置相应的插件HotModuleReplacementPlugin</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">HtmlWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      template<span class="token operator">:</span> <span class="token string">&quot;public/index.html&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 使用模板文件，主要用于查看效果</span>
      inject<span class="token operator">:</span> <span class="token string">&quot;head&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 插入到 head 标签中</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">new</span> <span class="token class-name">webpack<span class="token punctuation">.</span>HotModuleReplacementPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 热更新插件</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>生产环境配置文件： <code>webpack.pro.js</code></p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  mode<span class="token operator">:</span> <span class="token string">&quot;production&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 生产环境</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>公共配置文件： <code>webpack.common.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;path&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> CleanWebpackPlugin <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;clean-webpack-plugin&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> merge <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;webpack-merge&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> prodConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.pro&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 引入生产环境配置</span>
<span class="token keyword">const</span> devConfig <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./webpack.dev&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 引入开发环境配置</span>

<span class="token comment">// 公共配置</span>
<span class="token keyword">const</span> commonConfig <span class="token operator">=</span> <span class="token punctuation">{</span>
  entry<span class="token operator">:</span> <span class="token string">&quot;./src/axios.js&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 打包入口文件</span>
  output<span class="token operator">:</span> <span class="token punctuation">{</span>
    filename<span class="token operator">:</span> <span class="token string">&quot;axios.js&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 输出的文件名</span>
    path<span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">&quot;dist&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 输出的绝对路径</span>
    library<span class="token operator">:</span> <span class="token string">&quot;axios&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 类库的命名空间，如果通过网页的方式引入，则可以通过window.axios访问它</span>
    globalObject<span class="token operator">:</span> <span class="token string">&quot;this&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 定义全局变量,兼容node和浏览器运行，避免出现&quot;window is not defined&quot;的情况</span>
    libraryTarget<span class="token operator">:</span> <span class="token string">&quot;umd&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 定义打包方式Universal Module Definition,同时支持在CommonJS、AMD和全局变量使用</span>
    libraryExport<span class="token operator">:</span> <span class="token string">&quot;default&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 对外暴露default属性，就可以直接调用default里的属性</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  module<span class="token operator">:</span> <span class="token punctuation">{</span>
    rules<span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token comment">// 配置 babel 的解析，同时在项目的跟目录下有.babelrc的babel配置文件</span>
      <span class="token punctuation">{</span>
        test<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.js$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
        exclude<span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
        loader<span class="token operator">:</span> <span class="token string">&quot;babel-loader&quot;</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">CleanWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 每次打包都先清理出口（dist）文件夹</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">env</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 根据执行命令判断开发环境or生产环境，启用不同的配置文件</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>env <span class="token operator">&amp;&amp;</span> env<span class="token punctuation">.</span>production<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">merge</span><span class="token punctuation">(</span>commonConfig<span class="token punctuation">,</span> prodConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">merge</span><span class="token punctuation">(</span>commonConfig<span class="token punctuation">,</span> devConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p><code>.babelrc</code> 配置：</p><div class="language-javascript"><pre><code><span class="token punctuation">{</span>
  <span class="token string">&quot;plugins&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">[</span>
      <span class="token string">&quot;@babel/plugin-proposal-class-properties&quot;</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token string">&quot;loose&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">[</span>
      <span class="token string">&quot;@babel/plugin-transform-runtime&quot;</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token string">&quot;absoluteRuntime&quot;</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token string">&quot;corejs&quot;</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
        <span class="token string">&quot;helpers&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">&quot;regenerator&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">&quot;useESModules&quot;</span><span class="token operator">:</span> <span class="token boolean">false</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token string">&quot;presets&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">&quot;@babel/preset-env&quot;</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p><code>package.json</code> 增加 <code>script</code> 命令：</p><div class="language-javascript"><pre><code><span class="token string">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token string">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;webpack-dev-server --env.development --config webpack.common.js&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;webpack --env.production --config webpack.common.js&quot;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
复制代码
</code></pre></div><ul><li>开发时执行 <code>npm run dev</code> 命令；</li><li>打包时执行 <code>npm run build</code> 命令。</li></ul><p>至此 <code>axios</code> 库工程的基础配置已经好了。</p><h2 id="代码质量"><a class="header-anchor" href="#代码质量" aria-hidden="true">#</a> 代码质量</h2><p>以上配置能保证我们可以正确的把项目打包成类库了，但是企业级项目，代码质量管控也是非常重要的。</p><p>本项目将采用以下工具对代码质量进行管控：</p><ul><li><code>editorConfig</code> ，有助于维护跨多个编辑器和 <code>IDE</code> 从事同一项目的多个开发人员的一致编码风格。</li><li><code>ESLint</code> ，可组装的 <code>JavaScript</code> 和 <code>JSX</code> 检查工具。</li><li><code>husky</code> ，<code>git</code> 命令 <code>hook</code> 专用配置，它可以配置执行 <code>git commit</code> 等命令执行的钩子。</li><li><code>lint-staged</code> ，可以在特定的 <code>git</code> 阶段执行特定的命令。</li><li><code>prettier</code> ，代码统一格式化。</li><li><code>commitlint</code> ， <code>git commit message</code> 规范。</li></ul><h3 id="editorconfig"><a class="header-anchor" href="#editorconfig" aria-hidden="true">#</a> editorConfig</h3><p><code>.editorconfig</code> 配置：</p><div class="language-javascript"><pre><code>root <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token comment">// 根级配置</span>


<span class="token punctuation">[</span><span class="token operator">*</span><span class="token punctuation">]</span> <span class="token comment">// 指定作用于所有文件</span>
end_of_line <span class="token operator">=</span> lf <span class="token comment">// 定义换行符 [lf | cr | crlf]</span>
insert_final_newline <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token comment">// 文件是否以一个空白行结尾</span>

<span class="token punctuation">[</span><span class="token operator">*</span><span class="token punctuation">.</span><span class="token punctuation">{</span>js<span class="token punctuation">,</span>html<span class="token punctuation">,</span>css<span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token comment">// 作用文件</span>
charset <span class="token operator">=</span> utf<span class="token operator">-</span><span class="token number">8</span> <span class="token comment">// 编码格式</span>

<span class="token punctuation">[</span><span class="token operator">*</span><span class="token punctuation">.</span>js<span class="token punctuation">]</span> <span class="token comment">// 作用文件</span>
indent_style <span class="token operator">=</span> space <span class="token comment">// 缩进类型</span>
indent_size <span class="token operator">=</span> <span class="token number">2</span> <span class="token comment">// 缩进大小</span>
复制代码
</code></pre></div><h3 id="eslint"><a class="header-anchor" href="#eslint" aria-hidden="true">#</a> ESLint</h3><div class="language-javascript"><pre><code># 安装
npm install eslint <span class="token operator">--</span><span class="token constant">D</span> <span class="token operator">||</span> yarn add eslint <span class="token operator">-</span><span class="token constant">D</span>

# 初始化 eslint
<span class="token punctuation">.</span><span class="token operator">/</span>node_modules<span class="token operator">/</span><span class="token punctuation">.</span>bin<span class="token operator">/</span>eslint <span class="token operator">--</span>init <span class="token comment">// 问答式一步一步操作即可</span>

复制代码
</code></pre></div><p><code>.eslintrc.js</code> 配置：</p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  env<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// env 关键字指定你想启用的环境</span>
    browser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    es2021<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    node<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  parser<span class="token operator">:</span> <span class="token string">&quot;babel-eslint&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 解析器</span>
  <span class="token keyword">extends</span><span class="token operator">:</span> <span class="token string">&quot;eslint:recommended&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 继承的配置规则集</span>
  parserOptions<span class="token operator">:</span> <span class="token punctuation">{</span>
    ecmaVersion<span class="token operator">:</span> <span class="token number">12</span><span class="token punctuation">,</span> <span class="token comment">// 指定你想要使用的 ECMAScript 版本</span>
    sourceType<span class="token operator">:</span> <span class="token string">&quot;module&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 启用 ESModule</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  rules<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 规则 &quot;off&quot; = 关闭 &quot;warn&quot; = 警告 &quot;error&quot; = 报错</span>
    strict<span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 严格模式，规则关闭</span>
    <span class="token string">&quot;no-console&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 禁用 console 对象方法，规则关闭</span>
    <span class="token string">&quot;global-require&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 要求 require() 出现在顶层模块作用域中，规则关闭</span>
    <span class="token string">&quot;require-yield&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 要求 generator 函数内有 yield，规则关闭</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h3 id="husky-lint-staged-prettier"><a class="header-anchor" href="#husky-lint-staged-prettier" aria-hidden="true">#</a> husky + lint-staged + prettier</h3><p>安装：</p><div class="language-javascript"><pre><code>npm install husky lint<span class="token operator">-</span>staged prettier <span class="token operator">--</span>save<span class="token operator">-</span>dev
或
yarn add husky lint<span class="token operator">-</span>staged prettier <span class="token operator">--</span>dev
复制代码
</code></pre></div><p><code>.huskyrc</code> 配置：</p><div class="language-javascript"><pre><code><span class="token punctuation">{</span>
  <span class="token string">&quot;hooks&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string">&quot;pre-commit&quot;</span><span class="token operator">:</span> <span class="token string">&quot;lint-staged&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>该配置表示执行 <code>commit</code> 之前，先执行 <code>lint-staged</code> 。</p><p><code>.lintstagedrc</code> 配置：</p><div class="language-javascript"><pre><code><span class="token punctuation">{</span>
  <span class="token string">&quot;*.{js,ts,jsx,tsx}&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token string">&quot;eslint --fix --quiet&quot;</span><span class="token punctuation">,</span> <span class="token comment">// fix = 自动修复，quiet = ESLint报告错误</span>
    <span class="token string">&quot;prettier --write&quot;</span> <span class="token comment">// 使用 prettier 进行格式化</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token string">&quot;*.css&quot;</span><span class="token operator">:</span> <span class="token string">&quot;prettier --write&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;*.html&quot;</span><span class="token operator">:</span> <span class="token string">&quot;prettier --write&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;*.md&quot;</span><span class="token operator">:</span> <span class="token string">&quot;prettier --write&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;*.json&quot;</span><span class="token operator">:</span> <span class="token string">&quot;prettier --write&quot;</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>该配置表示 <code>lint-staged</code> 命令集。</p><h3 id="commitlint"><a class="header-anchor" href="#commitlint" aria-hidden="true">#</a> commitlint</h3><p>安装：</p><div class="language-javascript"><pre><code>npm install @commitlint<span class="token operator">/</span>config<span class="token operator">-</span>conventional @commitlint<span class="token operator">/</span>cli <span class="token operator">--</span>save<span class="token operator">-</span>dev
或
yarn add @commitlint<span class="token operator">/</span>config<span class="token operator">-</span>conventional @commitlint<span class="token operator">/</span>cli <span class="token operator">--</span>dev
复制代码
</code></pre></div><p><code>commitlint.config.js</code> 配置：</p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token keyword">extends</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;@commitlint/config-conventional&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p><code>.huskyrc</code> 中增加配置：</p><div class="language-javascript"><pre><code><span class="token punctuation">{</span>
  <span class="token string">&quot;hooks&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token operator">+</span>   <span class="token string">&quot;commit-msg&quot;</span><span class="token operator">:</span> <span class="token string">&quot;commitlint -E HUSKY_GIT_PARAMS&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 表示对 commit message 进行 commitlint 检测</span>
    <span class="token string">&quot;pre-commit&quot;</span><span class="token operator">:</span> <span class="token string">&quot;lint-staged&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>全部配置好后，我们在 <code>axios.js</code> 中添加如下代码来测试下：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>执行 <code>git add . &amp;&amp; git commit -m &quot;test&quot;</code> ，看看输出效果：</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c468f8cce2b94d0a8b9471e6cb71a587~tplv-k3u1fbpfcp-watermark.awebp" alt="QQ20201221-103629.gif"></p><p>可以看到项目会进行 <code>ESLint</code> 以及 <code>commitlint</code> 检查，这里的 <code>commit message</code> 编写的有问题，我们修改正确就可以正常提交到 <code>git</code> 仓库了。</p><h2 id="项目目录结构"><a class="header-anchor" href="#项目目录结构" aria-hidden="true">#</a> 项目目录结构</h2><div class="language-javascript"><pre><code>├──<span class="token constant">README</span><span class="token punctuation">.</span>md <span class="token comment">// 文档说明</span>
├──node_modules <span class="token comment">// 依赖包文件夹</span>
├──<span class="token keyword">package</span><span class="token punctuation">.</span>json
├──<span class="token punctuation">.</span>babelrc <span class="token comment">// babel 配置文件</span>
├──commitlint<span class="token punctuation">.</span>config<span class="token punctuation">.</span>js <span class="token comment">// commitlint 配置文件</span>
├──<span class="token punctuation">.</span>editorconfig <span class="token comment">// editorconfig 配置文件</span>
├──<span class="token punctuation">.</span>eslintrc<span class="token punctuation">.</span>js <span class="token comment">// ESLint 配置文件</span>
├──<span class="token punctuation">.</span>huskyrc <span class="token comment">// husky 配置文件</span>
├──<span class="token punctuation">.</span>lintstagedrc <span class="token comment">// lint-staged 配置文件</span>
├──webpack<span class="token punctuation">.</span>common<span class="token punctuation">.</span>js <span class="token comment">// webpack 公用配置</span>
├──webpack<span class="token punctuation">.</span>dev<span class="token punctuation">.</span>js <span class="token comment">// webpack 开发环境配置</span>
├──webpack<span class="token punctuation">.</span>pro<span class="token punctuation">.</span>js <span class="token comment">// webpack 生产环境配置</span>
├──<span class="token punctuation">.</span>gitignore <span class="token comment">// git 上传忽略配置</span>
└──src
    └──axios<span class="token punctuation">.</span>js <span class="token comment">// 库入口文件</span>
复制代码
</code></pre></div><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F01_%E9%A1%B9%E7%9B%AE%E5%B7%A5%E7%A8%8B" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h2 id="axios-源码编写"><a class="header-anchor" href="#axios-源码编写" aria-hidden="true">#</a> axios 源码编写</h2><p><code>axios</code> 的很多设计思想值得我们借鉴引用，这也是我们手写 axios 库的主要学习目的之一。</p><h3 id="axios-基础框架"><a class="header-anchor" href="#axios-基础框架" aria-hidden="true">#</a> axios 基础框架</h3><p>编写 <code>axios</code> 的第一步，我们得先搭建一个简易的架子。然后一步一步完善其中的内容。</p><p>axios 库的调用方式：</p><div class="language-javascript"><pre><code># 调用 axios 的 <span class="token keyword">get</span> 方法发送请求，可以想象它还有 <span class="token constant">POST</span>，<span class="token constant">PUT</span>，<span class="token constant">DELETE</span> 等 <span class="token constant">HTTP</span> 协议支持的方法
axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&#39;/user?ID=12345&#39;</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

# 调用 axios 的 request 方法发送请求
axios<span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  url<span class="token operator">:</span><span class="token string">&quot;/user?ID=12345&quot;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

# 可以通过向 axios 传递相关配置来创建请求
<span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&#39;post&#39;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token string">&#39;/user/12345&#39;</span><span class="token punctuation">,</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    firstName<span class="token operator">:</span> <span class="token string">&#39;Fred&#39;</span><span class="token punctuation">,</span>
    lastName<span class="token operator">:</span> <span class="token string">&#39;Flintstone&#39;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

# 可以使用自定义配置新建一个 axios 实例
<span class="token keyword">const</span> instance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  baseURL<span class="token operator">:</span> <span class="token string">&#39;https://some-domain.com/api/&#39;</span><span class="token punctuation">,</span>
  timeout<span class="token operator">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>
  headers<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token string">&#39;X-Custom-Header&#39;</span><span class="token operator">:</span> <span class="token string">&#39;foobar&#39;</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码
</code></pre></div><ol><li>调用 <code>axios</code> 的 <code>get</code> 方法发送请求，不难想象它还有 <code>POST</code>，<code>PUT</code>，<code>DELETE</code> 等 <code>HTTP</code> 协议支持的方法。</li><li>调用 <code>axios</code> 的 <code>request</code> 方法发送请求。</li><li>可以通过向 <code>axios</code> 传递相关配置来创建请求。</li><li>可以使用自定义配置新建一个 <code>axios</code> 实例。</li></ol><p>通过这些使用方式，我们首先搭建一个可以支持这样使用的 <code>lion-axios</code> 基础框架。</p><p>创建： <code>src/axios.js </code></p><h4 id="_1、创建-axios-类"><a class="header-anchor" href="#_1、创建-axios-类" aria-hidden="true">#</a> 1、创建 Axios 类</h4><div class="language-javascript"><pre><code><span class="token keyword">class</span> <span class="token class-name">Axios</span> <span class="token punctuation">{</span>
  <span class="token comment">// 用来存储配置信息。</span>
  config <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 实例化时接收一个配置信息，并保存到config属性中。</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>config <span class="token operator">=</span> initConfig<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 该类有一个 request 方法，它可以用来发送请求</span>
  <span class="token function">request</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h3 id="_2、createinstance-方法"><a class="header-anchor" href="#_2、createinstance-方法" aria-hidden="true">#</a> 2、createInstance 方法</h3><p>用于实例化 <code>Axios</code> 类。</p><div class="language-javascript"><pre><code><span class="token keyword">function</span> <span class="token function">createInstance</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 创建 Axios 实例</span>
  <span class="token keyword">const</span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Axios</span><span class="token punctuation">(</span>initConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 变量 instance 保存了 Axios 类上的 request 方法，并使用上一步实例化的对象去接替该方法中的 this。</span>
  <span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token class-name">Axios</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 返回的其实是 request 方法</span>
  <span class="token keyword">return</span> instance<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="_3、对外提供-axios-实例"><a class="header-anchor" href="#_3、对外提供-axios-实例" aria-hidden="true">#</a> 3、对外提供 axios 实例</h4><div class="language-javascript"><pre><code><span class="token comment">// 默认参数对象，如果用户不传入 method，则默认为 git</span>
<span class="token keyword">const</span> defaults <span class="token operator">=</span> <span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;get&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> axios <span class="token operator">=</span> <span class="token function">createInstance</span><span class="token punctuation">(</span>defaults<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> axios<span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="_4、使用-axios"><a class="header-anchor" href="#_4、使用-axios" aria-hidden="true">#</a> 4、使用 axios</h4><p>分析下，此时的 <code>axios</code>：</p><ol><li><code>axios = createInstance(defaults);</code></li><li><code>createInstance = Axios.prototype.request = request(config){}</code></li></ol><p>因此它支持 <code>axios({...});</code> 这样调用了。</p><h4 id="_5、支持-axios-create-方法"><a class="header-anchor" href="#_5、支持-axios-create-方法" aria-hidden="true">#</a> 5、支持 axios.create 方法</h4><div class="language-javascript"><pre><code>axios<span class="token punctuation">.</span><span class="token function-variable function">create</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 合并默认配置与用户传入的配置，并作为 createInstance 的参数参入，</span>
  <span class="token comment">// 目前合并config未实现。因此直接传了 default 去创建实例。</span>
  <span class="token comment">// const initConfig = mergeConfig(config,defaults);</span>
  <span class="token keyword">return</span> <span class="token function">createInstance</span><span class="token punctuation">(</span>defaults<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p><code>create</code> 方法支持传入一个配置对象去创建一个新的 <code>Axios</code> 实例。</p><p>现在支持这样调用 <code>axios</code> 了：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> instance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码
</code></pre></div><h3 id="_6、支持-axios-request-与-axios-get-axios-post-axios-put-等形式调用"><a class="header-anchor" href="#_6、支持-axios-request-与-axios-get-axios-post-axios-put-等形式调用" aria-hidden="true">#</a> 6、支持 axios.request 与 axios.get | axios.post | axios.put 等形式调用</h3><p>首先 <code>Axios</code> 类新增 <code>get</code> | <code>post</code> | <code>delete</code> 等方法</p><div class="language-javascript"><pre><code><span class="token keyword">class</span> <span class="token class-name">Axios</span> <span class="token punctuation">{</span>
  config <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>config <span class="token operator">=</span> initConfig<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">request</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token keyword">delete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">head</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">options</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">post</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">put</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">patch</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p><strong>instance 继承 Axios 类的方法及属性</strong></p><p>实现 <code>extend</code> 方法继承 <code>Axios</code> 实例上的方法及属性：</p><div class="language-javascript"><pre><code><span class="token keyword">function</span> <span class="token function">extend</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span> from<span class="token punctuation">,</span> ctx</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 继承方法</span>
  Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>from<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    to<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> from<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 继承 ctx 自身属性（不继承原型链上属性，因此需要 hasOwnProperty 进行判断）</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> val <span class="token keyword">in</span> ctx<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      to<span class="token punctuation">[</span>val<span class="token punctuation">]</span> <span class="token operator">=</span> ctx<span class="token punctuation">[</span>val<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> to<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>关于 <code>class</code> 实例方法继承有个小坑，详情查看<a href="https://link.juejin.cn/?target=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F30881632%2Fes6-iterate-over-class-methods" target="_blank" rel="noopener noreferrer">ES6 Iterate over class methods</a>。</p><p><code>createInstance</code> 中调用 <code>extend</code> 方法：</p><div class="language-javascript"><pre><code><span class="token keyword">function</span> <span class="token function">createInstance</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Axios</span><span class="token punctuation">(</span>initConfig<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token class-name">Axios</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 如果想 axios.request、axios.get 调用</span>
  <span class="token comment">// 可以让 instance 继承 context 上的 request、get、post 等方法。</span>
  <span class="token function">extend</span><span class="token punctuation">(</span>instance<span class="token punctuation">,</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">return</span> instance<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

复制代码<span class="token punctuation">;</span>
</code></pre></div><p>这样一来 <code>instance</code> 就继承了 <code>Axios</code> 类上定义的属性及方法了。现在就可以直接这样调用了：</p><div class="language-javascript"><pre><code>axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/user?ID=12345&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

axios<span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span> url<span class="token operator">:</span> <span class="token string">&quot;/user?ID=12345&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>到此为止 4 种调用方式都已经支持了。并且 <code>axios</code> 库的基础框架也搭建好了。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F02_%E5%9F%BA%E7%A1%80%E6%A1%86%E6%9E%B6" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="发送真实请求"><a class="header-anchor" href="#发送真实请求" aria-hidden="true">#</a> 发送真实请求</h3><p>虽然我们已经可以通过各种方式调用 <code>axios</code>，但是那仅仅是调用它，并不能真正的向后台发送请求。</p><p>当我们使用 <code>axios({...})</code> 与 <code>axios.request({})</code> 调用时，实际都是在调用 <code>Axios</code> 类上的 <code>request</code> 方法。那么我们请求后台的任务就交给它来实现。</p><h4 id="xhr"><a class="header-anchor" href="#xhr" aria-hidden="true">#</a> XHR</h4><p>在浏览器端向后台发送请求，可以使用 <code>fetch</code> 或 <code>XMLHttpRequest</code>。由于 <code>fetch</code> 羽翼尚未丰满，它甚至不支持取消请求的操作。因此我们在浏览器端选择 <code>XMLHttpRequest</code>。</p><h5 id="xmlhttprequest"><a class="header-anchor" href="#xmlhttprequest" aria-hidden="true">#</a> XMLHttpRequest</h5><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FXMLHttpRequest" target="_blank" rel="noopener noreferrer">XMLHttpRequest</a>（<code>XHR</code>）对象用于与服务器交互。通过 <code>XMLHttpRequest</code> 可以在不刷新页面的情况下请求特定 <code>URL</code>，获取数据。这允许网页在不影响用户操作的情况下，更新页面的局部内容。<code>XMLHttpRequest</code> 在 <code>AJAX</code> 编程中被大量使用。</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">xhr</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 解构config，data 如果不传默认为null，method 不传默认为 get方法，url是必传参数</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> method <span class="token operator">=</span> <span class="token string">&quot;get&quot;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>
  <span class="token comment">// 实例化 XMLHttpRequest</span>
  <span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 初始化一个请求</span>
  request<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 发送请求</span>
  request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name">Axios</span> <span class="token punctuation">{</span>
  config <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>config <span class="token operator">=</span> initConfig<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">request</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">xhr</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>通过简单实现 <code>xhr</code> 方法，并在 <code>request</code> 方法中调用它，我们就可以向后台发送真实的请求了。</p><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;post&quot;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token string">&quot;https://reqres.in/api/users&quot;</span><span class="token punctuation">,</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;frankshi&quot;</span><span class="token punctuation">,</span>
    job<span class="token operator">:</span> <span class="token string">&quot;FE&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Freqres.in%2F" target="_blank" rel="noopener noreferrer">reqres.in/</a> 这个网站提供了很多公共接口供我们使用，因此本文将使用它来模拟接口请求。</p><p>打开浏览器控制台，可以看到请求已经发送成功。</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aa8fca82d7664efc96e436ec08e3c238~tplv-k3u1fbpfcp-watermark.awebp" alt="image.png"></p><h4 id="解析-param-参数"><a class="header-anchor" href="#解析-param-参数" aria-hidden="true">#</a> 解析 param 参数</h4><p>需求分析：</p><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&#39;get&#39;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token string">&#39;/base/get&#39;</span><span class="token punctuation">,</span>
  params<span class="token operator">:</span> <span class="token punctuation">{</span>
    foo<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&#39;bar&#39;</span><span class="token punctuation">,</span> <span class="token string">&#39;baz&#39;</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token parameter">参数为数组，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">=</span>bar<span class="token operator">&amp;</span>foo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">=</span>baz

params<span class="token operator">:</span> <span class="token punctuation">{</span>
  foo<span class="token operator">:</span> <span class="token punctuation">{</span>
    bar<span class="token operator">:</span> <span class="token string">&#39;baz&#39;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token parameter">参数为对象，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token operator">=</span><span class="token operator">%</span><span class="token number">7</span>B<span class="token operator">%</span><span class="token number">22</span><span class="token operator">:</span><span class="token operator">%</span><span class="token number">22</span>baz<span class="token operator">%</span><span class="token number">22</span><span class="token operator">%</span><span class="token number">7</span>D

params<span class="token operator">:</span> <span class="token punctuation">{</span>
  date<span class="token operator">:</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token parameter">参数为日期，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>date<span class="token operator">=</span><span class="token number">2020</span><span class="token operator">-</span><span class="token number">12</span><span class="token operator">-</span><span class="token number">24</span>T08<span class="token operator">:</span><span class="token number">00</span><span class="token operator">:</span><span class="token number">00.000</span>z

params<span class="token operator">:</span> <span class="token punctuation">{</span>
  foo<span class="token operator">:</span> <span class="token string">&#39;@:$, &#39;</span>
<span class="token punctuation">}</span>
<span class="token parameter">参数为特殊字符，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token operator">=</span>@<span class="token operator">:</span>$<span class="token operator">+</span>（空格转换成<span class="token operator">+</span>）

params<span class="token operator">:</span> <span class="token punctuation">{</span>
  foo<span class="token operator">:</span> <span class="token string">&#39;bar&#39;</span><span class="token punctuation">,</span>
  baz<span class="token operator">:</span> <span class="token keyword">null</span>
<span class="token punctuation">}</span>
<span class="token parameter">参数为空值将忽略它，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token operator">=</span>bar

<span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&#39;get&#39;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token string">&#39;/base/get#hash&#39;</span><span class="token punctuation">,</span>
  params<span class="token operator">:</span> <span class="token punctuation">{</span>
    foo<span class="token operator">:</span> <span class="token string">&#39;bar&#39;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token parameter"><span class="token constant">URL</span>中包含hash值的，请求的时候将丢弃，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token operator">=</span>bar

<span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&#39;get&#39;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token string">&#39;/base/get?foo=bar&#39;</span><span class="token punctuation">,</span>
  params<span class="token operator">:</span> <span class="token punctuation">{</span>
    bar<span class="token operator">:</span> <span class="token string">&#39;baz&#39;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token parameter"><span class="token constant">URL</span>中已经包含参数的，请求的时候将拼接上去，最终请求的<span class="token constant">URL</span>是</span> <span class="token operator">=&gt;</span> <span class="token operator">/</span>base<span class="token operator">?</span>foo<span class="token operator">=</span>bar<span class="token operator">&amp;</span>bar<span class="token operator">=</span>baz
复制代码
</code></pre></div><p>代码实现：</p><p>1、<code>request</code> 中增加一个方法来统一处理配置</p><div class="language-javascript"><pre><code><span class="token function">request</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 处理传入的配置</span>
  <span class="token function">processConfig</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 发送请求</span>
  <span class="token function">xhr</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>2、在 <code>processConfig</code> 中转换 <code>URL</code> 参数</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">buildURL</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">url<span class="token punctuation">,</span> params</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">transformURL</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> url<span class="token punctuation">,</span> params <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token function">buildURL</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> params<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">processConfig</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  config<span class="token punctuation">.</span>url <span class="token operator">=</span> <span class="token function">transformURL</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>3、处理 <code>params</code> 的主体函数：</p><div class="language-javascript"><pre><code><span class="token comment">// 判断是否Date对象</span>
<span class="token keyword">function</span> <span class="token function">isDate</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">toString</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">&quot;[object Date]&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 判断是否Object对象</span>
<span class="token keyword">function</span> <span class="token function">isPlainObject</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">toString</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">&quot;[object Object]&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 判断是否URLSearchParams对象实例</span>
<span class="token keyword">function</span> <span class="token function">isURLSearchParams</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">typeof</span> val <span class="token operator">!==</span> <span class="token string">&quot;undefined&quot;</span> <span class="token operator">&amp;&amp;</span> val <span class="token keyword">instanceof</span> <span class="token class-name">URLSearchParams</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> <span class="token function-variable function">buildURL</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">url<span class="token punctuation">,</span> params</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 如果 params 参数为空，则直接返回原 URL</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> url<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 定义一个变量，用来保存最终拼接后的参数</span>
  <span class="token keyword">let</span> serializedParams<span class="token punctuation">;</span>
  <span class="token comment">// 检测 params 是不是 URLSearchParams 对象类型</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isURLSearchParams</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 如果是（例如：new URLSearchParams(topic=api&amp;foo=bar)），则 params 直接序列化输出</span>
    serializedParams <span class="token operator">=</span> params<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token comment">// 如果不是则进入该主体运行</span>
    <span class="token comment">// 定义一个数组</span>
    <span class="token keyword">const</span> parts <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token comment">// Object.keys 可以获取一个对象的所有key的数组，通过 forEach 进行遍历</span>
    Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token comment">// 获取每个key对象的val</span>
      <span class="token keyword">const</span> val <span class="token operator">=</span> params<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token comment">// 如果 val 是 null，或者是 undefined 则终止这轮循环，进入下轮循环，这里就是忽略空值操作</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>val <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token keyword">typeof</span> val <span class="token operator">===</span> <span class="token string">&quot;undefined&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 定义一个数组</span>
      <span class="token keyword">let</span> values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token comment">// 判断 val 是否是一个数组类型</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 是的话，values空数组赋值为 val，并且 key 拼接上[]</span>
        values <span class="token operator">=</span> val<span class="token punctuation">;</span>
        key <span class="token operator">+=</span> <span class="token string">&quot;[]&quot;</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token comment">// val 不是数组的话，也让它变为数组，抹平数据类型不同的差异，方便后面统一处理</span>
        values <span class="token operator">=</span> <span class="token punctuation">[</span>val<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 由于前面抹平差异，这里可以统一当做数组进行处理</span>
      values<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token comment">// 如果 val 是日期对象，</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDate</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token comment">// toISOString返回Date对象的标准的日期时间字符串格式的字符串</span>
          val <span class="token operator">=</span> val<span class="token punctuation">.</span><span class="token function">toISOString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token comment">// 如果 val 是对象类型的话，直接序列化</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          val <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 处理结果推入数组</span>
        parts<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encode</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encode</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 最后拼接数组</span>
    serializedParams <span class="token operator">=</span> parts<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">&quot;&amp;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>serializedParams<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 处理 hash 的情况</span>
    <span class="token keyword">const</span> markIndex <span class="token operator">=</span> url<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">&quot;#&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>markIndex <span class="token operator">!==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      url <span class="token operator">=</span> url<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> markIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 处理，如果传入已经带有参数，则拼接在其后面，否则要手动添加上一个 ?</span>
    url <span class="token operator">+=</span> <span class="token punctuation">(</span>url<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">&quot;?&quot;</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">?</span> <span class="token string">&quot;?&quot;</span> <span class="token operator">:</span> <span class="token string">&quot;&amp;&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> serializedParams<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 输出完整的 URL</span>
  <span class="token keyword">return</span> url<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="解析-body-数据"><a class="header-anchor" href="#解析-body-数据" aria-hidden="true">#</a> 解析 body 数据</h4><p>前面发送 <code>body</code> 数据是这样做的 <code>request.send(data);</code> 。而 <code>data</code> 正是我们传入的 <code>data</code> 数据：</p><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;post&quot;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> baseURL<span class="token punctuation">,</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;frankshi&quot;</span><span class="token punctuation">,</span>
    job<span class="token operator">:</span> <span class="token string">&quot;FE&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>直接传入的 <code>data</code> 是不能直接作为 <code>send</code> 函数的入参，需要把它转换成 <code>JSON</code> 字符串。</p><p>详见：<a href="https://link.juejin.cn/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FXMLHttpRequest%2Fsend" target="_blank" rel="noopener noreferrer">MDN XMLHttpRequest send</a></p><p>实现：</p><div class="language-javascript"><pre><code><span class="token comment">// 该函数是对 data 进行序列化</span>
<span class="token keyword">const</span> <span class="token function-variable function">transformRequest</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> data<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 定义一个函数，它的职责是处理请求的数据</span>
<span class="token keyword">const</span> <span class="token function-variable function">transformRequestData</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">transformRequest</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// processConfig 是前面定义的，用于处理传入的配置，它之前已经处理了URL，这里再增加处理data</span>
<span class="token keyword">const</span> <span class="token function-variable function">processConfig</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  config<span class="token punctuation">.</span>url <span class="token operator">=</span> <span class="token function">transformURL</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
  config<span class="token punctuation">.</span>data <span class="token operator">=</span> <span class="token function">transformRequestData</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="解析-header"><a class="header-anchor" href="#解析-header" aria-hidden="true">#</a> 解析 header</h4><p>一次 <code>HTTP</code> 请求中，<code>header</code> 扮演着非常重要的角色，它是客户端与服务端之间能互相理解对方的桥梁。因此我们的库也必须要保证可以正确传递并解析 <code>header</code>。</p><h5 id="默认添加上-content-type"><a class="header-anchor" href="#默认添加上-content-type" aria-hidden="true">#</a> 默认添加上 Content-Type</h5><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;post&quot;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> baseURL<span class="token punctuation">,</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;frankshi&quot;</span><span class="token punctuation">,</span>
    job<span class="token operator">:</span> <span class="token string">&quot;FE&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>当前端这样发送请求时，我们需要告诉服务器，我们发送的 <code>data</code> 的数据类型以便服务器可以正确解析。</p><p>因此需要如此配置：</p><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;post&quot;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> baseURL<span class="token punctuation">,</span>
  headers<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string">&quot;content-type&quot;</span><span class="token operator">:</span> <span class="token string">&quot;application/json;charset=utf-8&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;frankshi&quot;</span><span class="token punctuation">,</span>
    job<span class="token operator">:</span> <span class="token string">&quot;FE&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>如果用户不添加 <code>content-type</code> ，我们需要为它自动添加上该配置。</p><p>接下来我们就来实现关于 <code>headers</code> 的逻辑：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">normalizeHeaderName</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">headers<span class="token punctuation">,</span> normalizedName</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>headers<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 遍历所有 headers</span>
  Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>headers<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 处理这种情况 如果 name 是 content-type，normalizedName 是 Content-Type，则统一使用 Content-Type</span>
    <span class="token comment">// 并且删除 content-type。</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>
      name <span class="token operator">!==</span> normalizedName <span class="token operator">&amp;&amp;</span>
      name<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> normalizedName<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span> <span class="token punctuation">{</span>
      headers<span class="token punctuation">[</span>normalizedName<span class="token punctuation">]</span> <span class="token operator">=</span> headers<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token keyword">delete</span> headers<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">processHeaders</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">headers<span class="token punctuation">,</span> data</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">normalizeHeaderName</span><span class="token punctuation">(</span>headers<span class="token punctuation">,</span> <span class="token string">&quot;Content-Type&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 判断如果data数据是一个对象，则设置上&#39;Content-Type&#39;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>headers <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>headers<span class="token punctuation">[</span><span class="token string">&quot;Content-Type&quot;</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      headers<span class="token punctuation">[</span><span class="token string">&quot;Content-Type&quot;</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">&quot;application/json;charset=utf-8&quot;</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> headers<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">transformHeaders</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> headers <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token function">processHeaders</span><span class="token punctuation">(</span>headers<span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">processConfig</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  config<span class="token punctuation">.</span>url <span class="token operator">=</span> <span class="token function">transformURL</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
  config<span class="token punctuation">.</span>headers <span class="token operator">=</span> <span class="token function">transformHeaders</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
  config<span class="token punctuation">.</span>data <span class="token operator">=</span> <span class="token function">transformRequestData</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">xhr</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> <span class="token punctuation">{</span> data <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> method <span class="token operator">=</span> <span class="token string">&quot;get&quot;</span><span class="token punctuation">,</span> headers <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>
  <span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  request<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 遍历所有处理后的 headers</span>
  Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>headers<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 如果 data 为空的话，则删除 content-type</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>data <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> name<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">&quot;content-type&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">delete</span> headers<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token comment">// 给请求设置上 header</span>
      request<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> headers<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="响应请求"><a class="header-anchor" href="#响应请求" aria-hidden="true">#</a> 响应请求</h4><p>上面的请求，解析了 <code>Request</code> 请求的 <code>param</code>、<code>body</code>、<code>header</code>。但是客户端并不能获取到后台响应的数据。接下来我们就来实现基于 <code>Promise</code> 的数据响应。</p><p>改造 <code>xhr</code> 函数</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">xhr</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
  <span class="token comment">// 所有的实现同构 new Promise 包裹起来</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
    <span class="token keyword">let</span> <span class="token punctuation">{</span>data <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span>url<span class="token punctuation">,</span>method <span class="token operator">=</span> <span class="token string">&#39;get&#39;</span><span class="token punctuation">,</span>headers<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> responseType<span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>

    <span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    request<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>url<span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 判断用户是否设置了返回数据类型</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>responseType<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      request<span class="token punctuation">.</span>responseType <span class="token operator">=</span> responseType
    <span class="token punctuation">}</span>
    <span class="token comment">// 监听 onreadystatechange 函数，接收后台返回数据</span>
    request<span class="token punctuation">.</span><span class="token function-variable function">onreadystatechange</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>request<span class="token punctuation">.</span>readyState <span class="token operator">!==</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span>
      <span class="token punctuation">}</span>

      <span class="token keyword">if</span> <span class="token punctuation">(</span>request<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 返回的 header 是字符串类型，通过 parseHeaders 解析成对象类型</span>
      <span class="token keyword">const</span> responseHeaders <span class="token operator">=</span> <span class="token function">parseHeaders</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getAllResponseHeaders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> responseData <span class="token operator">=</span>
        responseType <span class="token operator">&amp;&amp;</span> responseType <span class="token operator">!==</span> <span class="token string">&#39;text&#39;</span> <span class="token operator">?</span> request<span class="token punctuation">.</span>response <span class="token operator">:</span> request<span class="token punctuation">.</span>responseText
      <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token punctuation">{</span>
        data<span class="token operator">:</span> responseData<span class="token punctuation">,</span>
        status<span class="token operator">:</span> request<span class="token punctuation">.</span>status<span class="token punctuation">,</span>
        statusText<span class="token operator">:</span> request<span class="token punctuation">.</span>statusText<span class="token punctuation">,</span>
        headers<span class="token operator">:</span> responseHeaders<span class="token punctuation">,</span>
        config<span class="token punctuation">,</span>
        request
      <span class="token punctuation">}</span>
      <span class="token comment">// 通过 resolve 返回数据</span>
      <span class="token function">resolve</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 遍历所有处理后的 headers</span>
    <span class="token operator">...</span> 省略了 headers 的处理
    request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>这样写完之后，我们在提供给用户使用时，就可以写 <code>Promise</code> 语法了。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F03_%E7%9C%9F%E5%AE%9E%E8%AF%B7%E6%B1%82" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="目录结构调整"><a class="header-anchor" href="#目录结构调整" aria-hidden="true">#</a> 目录结构调整</h3><p>现在有一个最大的问题就是，所有代码都集中在 <code>axios.js</code> 文件中，对于一个专业库来说，合理的文件目录划分是相当重要的。</p><p>优化后的目录结构图谱：</p><div class="language-javascript"><pre><code>└──src
    └──axios<span class="token punctuation">.</span>js <span class="token comment">// 入口文件</span>
    └──core <span class="token comment">// 核心文件夹</span>
        └──Axios<span class="token punctuation">.</span>js <span class="token comment">// 存放 Axios 类</span>
        └──dispatchRequest<span class="token punctuation">.</span>js <span class="token comment">// 触发请求</span>
    └──adapters <span class="token comment">// 适配器文件夹，axios 可以适配 node 中的 http，浏览器中的 xhr</span>
        └──xhr<span class="token punctuation">.</span>js <span class="token comment">// 浏览器 xhr 请求</span>
        └──http<span class="token punctuation">.</span>js <span class="token comment">// node http 请求，目前暂未实现</span>
    └──helpers <span class="token comment">// 存放工具函数</span>
    	└──data<span class="token punctuation">.</span>js <span class="token comment">// 转换数据相关函数</span>
    	└──headers<span class="token punctuation">.</span>js <span class="token comment">// 处理 header 相关函数</span>
    	└──url<span class="token punctuation">.</span>js <span class="token comment">// 处理 url 相关函数</span>
    	└──util<span class="token punctuation">.</span>js <span class="token comment">// 通用工具函数</span>
复制代码
</code></pre></div><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F04_%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="适配器"><a class="header-anchor" href="#适配器" aria-hidden="true">#</a> 适配器</h3><p>为了支持不同的环境，<code>Axios</code> 引入了适配器。在上小节整理目录的时候已经增加了 <code>dispatchRequest.js</code> 文件，它的主要职责是发送请求，适配器写在这里也最为合适。</p><div class="language-"><pre><code>dispatchRequest.js
// 默认适配器，判断是否有 XMLHttpRequest，来决定是否为浏览器环境，从而判断选择 xhr 还是 http
const getDefaultAdapter = () =&gt; {
  let adapter;
  if (typeof XMLHttpRequest !== &#39;undefined&#39;) {
    // 浏览器
    adapter = require(&quot;../adapters/xhr&quot;);
  } else if (typeof process !== &#39;undefined&#39; &amp;&amp; Object.prototype.toString.call(process) === &#39;[object process]&#39;) {
    // node.js
    adapter = require(&quot;../adapters/http&quot;);
  }
  return adapter;
}

const dispatchRequest = (config) =&gt; {
  // 如果用户传入了适配器则使用用户的，否则使用默认的适配器(后期会提取到默认配置文件)
  const adapter = config.adapter || getDefaultAdapter();

  // 处理传入的配置
  processConfig(config);
  // 发送请求
  return adapter(config).then((res) =&gt; transformResponseData(res));
};
复制代码
</code></pre></div><p>适配器的原理不复杂，<code>axios</code> 就是通过这个函数抹平了浏览器和 <code>node.js</code> 环境的使用差异，做到用户无感知的。这里有个细节那就是 <code>adapter = require(&quot;../adapters/*&quot;)</code> ，为什么这里又使用了 <code>CommonJS</code> 的 <code>require</code> 呢？</p><p>是的，作者把这里 <code>xhr</code> 与 <code>http</code>的导出改为了 <code>module.exports</code> 的形式：</p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token function">httpAdapter</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;httpAdapter&quot;</span><span class="token punctuation">,</span> config<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>为什么使用 <code>require</code> 而不使用 <code>import</code> 呢？</p><blockquote><p>是因为 CommonJS 加载的是一个对象（即 module.exports 属性），该对象只有在脚本运行完才会生成。而 ES6 模块不是对象，它的对外接口只是一种静态定义，在代码静态解析阶段就会生成。</p></blockquote><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F05_%E9%80%82%E9%85%8D%E5%99%A8" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="完善-axios-类方法"><a class="header-anchor" href="#完善-axios-类方法" aria-hidden="true">#</a> 完善 Axios 类方法</h3><p>之前 <code>Axios</code> 类中定义了很多方法，但是都没有具体实现，现在 <code>request</code> 方法已经实现了，其它方法实现起来也就相对简单。</p><div class="language-"><pre><code>Axios.js
import dispatchRequest from &quot;./dispatchRequest&quot;;

class Axios {
  config = {};

  constructor(initConfig) {
    this.config = initConfig;
  }

  request(config) {
    return dispatchRequest(config);
  }

  get(url,config) {
    return this._requestMethodWithoutData(&#39;get&#39;,url,config)
  }

  delete(url,config) {
    return this._requestMethodWithoutData(&#39;delete&#39;, url, config)
  }

  head(url,config) {
    return this._requestMethodWithoutData(&#39;head&#39;, url, config)
  }

  options(url,config) {
    return this._requestMethodWithoutData(&#39;head&#39;, url, config)
  }

  post(url,data,config) {
    return this._requestMethodWithData(&#39;post&#39;, url, data, config)
  }

  put(url,data,config) {
    return this._requestMethodWithData(&#39;put&#39;, url, data, config)
  }

  patch(url,data,config) {
    return this._requestMethodWithData(&#39;patch&#39;, url, data, config)
  }
  // 通用不带Data的调用方法，其本质就是调用 require 方法
  _requestMethodWithoutData(
    method,
    url,
    config
  ) {
    return this.request(
      Object.assign(config || {}, {
        method,
          url
      })
    )
  }
  // 通用带Data调用方法
  _requestMethodWithData(
    method,
    url,
    data,
    config
  ) {
    // 合并参数
    return this.request(
      Object.assign(config || {}, {
        method,
        url,
        data
      })
    )
  }
}

export default Axios;
复制代码
</code></pre></div><p>如此完善后，我们就可以通过 <code>axios.get()</code>、<code>axios.post()</code> 等方式调用了。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F07_%E5%AE%8C%E5%96%84Axios" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="拦截器"><a class="header-anchor" href="#拦截器" aria-hidden="true">#</a> 拦截器</h3><h4 id="拦截器简介"><a class="header-anchor" href="#拦截器简介" aria-hidden="true">#</a> 拦截器简介</h4><p>在请求或响应被 <code>then</code> 或 <code>catch</code> 处理前拦截它们。</p><div class="language-javascript"><pre><code><span class="token comment">// 添加请求拦截器</span>
axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>
  <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 在发送请求之前做些什么</span>
    <span class="token keyword">return</span> config<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 对请求错误做些什么</span>
    <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 添加响应拦截器</span>
axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>
  <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 对响应数据做点什么</span>
    <span class="token keyword">return</span> response<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 对响应错误做点什么</span>
    <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><ul><li>请求拦截器：它的作用是在请求发送前统一执行某些操作，比如在请求头中添加 <code>token</code> 字段。</li><li>响应拦截器：它的作用是在接收到服务器响应后统一执行某些操作，比如发现响应状态码为 <code>401</code> 时，自动跳转到登录页。</li></ul><p>此外我们还可以删除某个拦截器：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> myInterceptor <span class="token operator">=</span> axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">/*...*/</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">eject</span><span class="token punctuation">(</span>myInterceptor<span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>到此，你会发现其实拦截器非常像 <code>redux</code>、<code>koa2</code> 的中间件。它会按照你的添加顺序，先去执行<code>request</code>拦截器，然后执行真实请求，最后再执行 <code>response</code> 拦截器。</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0cea6068419f4621bc1cff5e7b411875~tplv-k3u1fbpfcp-watermark.awebp" alt="image.png"></p><h4 id="拦截器实现"><a class="header-anchor" href="#拦截器实现" aria-hidden="true">#</a> 拦截器实现</h4><p>创建拦截器管理类： <code>core/InterceptorManager.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">InterceptorManager</span> <span class="token punctuation">{</span>
  <span class="token comment">// 定义一个数组，用来存储拦截器</span>
  interceptors <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">resolved<span class="token punctuation">,</span> rejected</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 向数组推入拦截器对象</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      resolved<span class="token punctuation">,</span>
      rejected<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 返回拦截器在数组中索引</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 遍历数组</span>
  <span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">interceptor</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>interceptor <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">fn</span><span class="token punctuation">(</span>interceptor<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 根据索引删除拦截器</span>
  <span class="token function">eject</span><span class="token punctuation">(</span><span class="token parameter">id</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">[</span>id<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>这个拦截器类其实就是一个数组并且实现了 use、forEach、eject 3 个方法去操作它。</p><p>接下来要实现拦截器的链式调用：<code>core/Axios.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">class</span> <span class="token class-name">Axios</span> <span class="token punctuation">{</span>
  config <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token comment">// 定义一个拦截器对象</span>
  interceptors <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">initConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>config <span class="token operator">=</span> initConfig<span class="token punctuation">;</span>
    <span class="token comment">// 拦截器对象中包含：request拦截器以及response拦截器</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors <span class="token operator">=</span> <span class="token punctuation">{</span>
      request<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">InterceptorManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      response<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">InterceptorManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">request</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 定义一个数组，数组中放入，会发送真实请求的对象，可以想象成它也是一个拦截器</span>
    <span class="token keyword">const</span> chain <span class="token operator">=</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span>
        resolved<span class="token operator">:</span> dispatchRequest<span class="token punctuation">,</span>
        rejected<span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token comment">// 当用户使用 axios.interceptors.request.use(...) 推入了多个请求拦截器时</span>
    <span class="token comment">// this.interceptors.request 这里面就有多个拦截器，通过遍历拦截器，插入 chain 数组的前面</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">interceptor</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      chain<span class="token punctuation">.</span><span class="token function">unshift</span><span class="token punctuation">(</span>interceptor<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 当用户使用 axios.interceptors.response.use(...) 推入多个响应拦截器时</span>
    <span class="token comment">// this.interceptors.response 这里面就有多个拦截器，通过遍历拦截器，插入 chain 数组的后面</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">interceptor</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      chain<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>interceptor<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 此时的 chain 应该是这样的</span>
    <span class="token comment">/*
    [
    	{
      	resolved:(config)=&gt;{...}, // 用户自定义请求拦截器
        rejected:(config)=&gt;{...}
      },
      ...
      {
        resolved: dispatchRequest,
        rejected: undefined
      },
      ...
      {
      	resolved:(res)=&gt;{...}, // 用户自定义响应拦截器
        rejected:(res)=&gt;{...}
      },
    ]
    */</span>

    <span class="token keyword">let</span> promise <span class="token operator">=</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 如果 chain 数组中有值就进入循环遍历</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span>chain<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 每次取出数组的第一个元素，并从数组中删除</span>
      <span class="token keyword">const</span> <span class="token punctuation">{</span> resolved<span class="token punctuation">,</span> rejected <span class="token punctuation">}</span> <span class="token operator">=</span> chain<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// promise 复制为下一次 promise.then，实现拦截器链式传递</span>
      promise <span class="token operator">=</span> promise<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>resolved<span class="token punctuation">,</span> rejected<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 最终全部执行完成之后，返回最后的执行结果</span>
    <span class="token keyword">return</span> promise<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>拦截器的实现思想非常值得借鉴，它的实现并不算复杂，巧妙的应用了 <code>promise</code> 不断传递，让 <code>axios</code> 具备了类似“中间件”的功能。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F08_%E6%8B%A6%E6%88%AA%E5%99%A8" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="默认配置"><a class="header-anchor" href="#默认配置" aria-hidden="true">#</a> 默认配置</h3><h4 id="默认配置使用介绍"><a class="header-anchor" href="#默认配置使用介绍" aria-hidden="true">#</a> 默认配置使用介绍</h4><h5 id="全局的-axios-默认值"><a class="header-anchor" href="#全局的-axios-默认值" aria-hidden="true">#</a> 全局的 axios 默认值</h5><div class="language-javascript"><pre><code>axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>baseURL <span class="token operator">=</span> <span class="token string">&quot;https://api.example.com&quot;</span><span class="token punctuation">;</span>
axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>common<span class="token punctuation">[</span><span class="token string">&quot;Authorization&quot;</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token constant">AUTH_TOKEN</span><span class="token punctuation">;</span>
axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>post<span class="token punctuation">[</span><span class="token string">&quot;Content-Type&quot;</span><span class="token punctuation">]</span> <span class="token operator">=</span>
  <span class="token string">&quot;application/x-www-form-urlencoded&quot;</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h5 id="自定义实例默认值"><a class="header-anchor" href="#自定义实例默认值" aria-hidden="true">#</a> 自定义实例默认值</h5><div class="language-javascript"><pre><code><span class="token comment">// Set config defaults when creating the instance</span>
<span class="token keyword">const</span> instance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  baseURL<span class="token operator">:</span> <span class="token string">&quot;https://api.example.com&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Alter defaults after instance has been created</span>
instance<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>common<span class="token punctuation">[</span><span class="token string">&quot;Authorization&quot;</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token constant">AUTH_TOKEN</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="默认配置实现"><a class="header-anchor" href="#默认配置实现" aria-hidden="true">#</a> 默认配置实现</h4><h5 id="创建-defaults-配置文件"><a class="header-anchor" href="#创建-defaults-配置文件" aria-hidden="true">#</a> 创建 defaults 配置文件</h5><div class="language-"><pre><code>src/defaults.js
const defaults = {
  method: &#39;get&#39;, // 默认不传入 method 则给一个 GET 方法

  timeout: 0, // 默认不设置超时

  headers: {
    common: {
      Accept: &#39;application/json, text/plain, */*&#39; // 默认给一个 Accept header
    }
  }
}
// &#39;delete&#39;, &#39;get&#39;, &#39;head&#39;, &#39;options&#39; 这四种类型请求时默认 headers 为空
const methodsNoData = [&#39;delete&#39;, &#39;get&#39;, &#39;head&#39;, &#39;options&#39;]

methodsNoData.forEach(method =&gt; {
  defaults.headers[method] = {}
})

// &#39;post&#39;, &#39;put&#39;, &#39;patch&#39; 这三种类型请求时设置一个默认的 Content-Type
const methodsWithData = [&#39;post&#39;, &#39;put&#39;, &#39;patch&#39;]

methodsWithData.forEach(method =&gt; {
  defaults.headers[method] = {
    &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded&#39;
  }
})

export default defaults
复制代码
</code></pre></div><h4 id="axios-中进行配置合并"><a class="header-anchor" href="#axios-中进行配置合并" aria-hidden="true">#</a> axios 中进行配置合并</h4><div class="language-"><pre><code>Axios.js
...
import mergeConfig from &quot;./mergeConfig&quot;;

class Axios {
  defaults = {};

  constructor(initConfig) {
    this.defaults = initConfig;
    ...
  }

  request(url, config) {
    if (typeof url === &quot;string&quot;) {
      if (!config) {
        config = {};
      }
      config.url = url;
    } else {
      config = url;
    }
    // 合并默认配置与用户传进来的配置
    config = mergeConfig(this.defaults, config)

    ...
  }
}

复制代码
</code></pre></div><p>假设有如下两个配置文件需要合并：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> defaults <span class="token operator">=</span> <span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&#39;get&#39;</span><span class="token punctuation">,</span>
  timeout<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  headers<span class="token operator">:</span> <span class="token punctuation">{</span>
    common<span class="token operator">:</span> <span class="token punctuation">{</span>
      Accept<span class="token operator">:</span> <span class="token string">&#39;application/json, text/plain, */*&#39;</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    post<span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token string">&#39;Content-Type&#39;</span><span class="token operator">:</span> <span class="token string">&#39;application/x-www-form-urlencoded&#39;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> customConfig <span class="token operator">=</span> <span class="token punctuation">{</span>
  url<span class="token operator">:</span> <span class="token string">&quot;/post&quot;</span><span class="token punctuation">,</span>
  method<span class="token operator">:</span> <span class="token string">&quot;post&quot;</span><span class="token punctuation">,</span>
  data<span class="token operator">:</span><span class="token punctuation">{</span>a<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
  headers<span class="token operator">:</span><span class="token punctuation">{</span>
    test<span class="token operator">:</span> <span class="token string">&quot;123&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>只要 <code>customConfig</code> 中有的配置优先使用用户自己的配置，否则才去使用默认配置。</p><p>合并配置的具体实现：<code>src/core/mergeConfig.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">import</span> <span class="token punctuation">{</span> deepMerge<span class="token punctuation">,</span> isPlainObject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;../helpers/util&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 创建策略对象</span>
<span class="token keyword">const</span> strats <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 默认策略：val2即用户配置不为空则使用用户配置，否则使用val1默认配置</span>
<span class="token keyword">function</span> <span class="token function">defaultStrat</span><span class="token punctuation">(</span><span class="token parameter">val1<span class="token punctuation">,</span> val2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">typeof</span> val2 <span class="token operator">!==</span> <span class="token string">&quot;undefined&quot;</span> <span class="token operator">?</span> val2 <span class="token operator">:</span> val1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// &#39;url&#39;, &#39;params&#39;, &#39;data&#39; 这三种属性，只使用用户配置</span>
<span class="token keyword">function</span> <span class="token function">fromVal2Strat</span><span class="token punctuation">(</span><span class="token parameter">val1<span class="token punctuation">,</span> val2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> val2 <span class="token operator">!==</span> <span class="token string">&quot;undefined&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> val2<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 深度合并配置，适用于配置本身是对象的情况</span>
<span class="token comment">// deepMerge 方法就是对象的深度拷贝的方法，这里就不再陈列赘述了</span>
<span class="token keyword">function</span> <span class="token function">deepMergeStrat</span><span class="token punctuation">(</span><span class="token parameter">val1<span class="token punctuation">,</span> val2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>val2<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">deepMerge</span><span class="token punctuation">(</span>val1<span class="token punctuation">,</span> val2<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> val2 <span class="token operator">!==</span> <span class="token string">&quot;undefined&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> val2<span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>val1<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">deepMerge</span><span class="token punctuation">(</span>val1<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> val1<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 使用用户配置的三种属性</span>
<span class="token keyword">const</span> stratKeysFromVal2 <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">&quot;url&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;params&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;data&quot;</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

stratKeysFromVal2<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  strats<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> fromVal2Strat<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 需要深度合并的两种属性</span>
<span class="token keyword">const</span> stratKeysDeepMerge <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">&quot;headers&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;auth&quot;</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

stratKeysDeepMerge<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  strats<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> deepMergeStrat<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">mergeConfig</span><span class="token punctuation">(</span><span class="token parameter">config1<span class="token punctuation">,</span> config2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>config2<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    config2 <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 创建一个空对象，用来存储最终合并好的配置文件</span>
  <span class="token keyword">const</span> config <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 遍历用户配置，mergeField方法就是根据属性选择到不同的策略，进行合并配置</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">in</span> config2<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">mergeField</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 遍历默认配置，并且该配置没有在用户配置中出现的</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">in</span> config1<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>config2<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">mergeField</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 这里采用的是设计模式中的“策略模式”，有效的剔除了代码中无限个 if else 的情况</span>
  <span class="token keyword">function</span> <span class="token function">mergeField</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> strat <span class="token operator">=</span> strats<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">||</span> defaultStrat<span class="token punctuation">;</span>
    config<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">strat</span><span class="token punctuation">(</span>config1<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">,</span> config2<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 导出最终的config</span>
  <span class="token keyword">return</span> config<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

复制代码<span class="token punctuation">;</span>
</code></pre></div><p>这样一来我们就实现了 <code>axios</code> 的配置合并，这里的代码比较复杂，处理的情况较多，因此如果想要完全理解这块代码需要实际去运行代码，能理解这里的核心思想，使用策略模式消除了代码中的 <code>if...else</code> 是最为重要的。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F09_%E9%BB%98%E8%AE%A4%E9%85%8D%E7%BD%AE" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="取消功能"><a class="header-anchor" href="#取消功能" aria-hidden="true">#</a> 取消功能</h3><h4 id="取消请求需求分析"><a class="header-anchor" href="#取消请求需求分析" aria-hidden="true">#</a> 取消请求需求分析</h4><p>使用 <code>cancel token</code> 取消请求，可以使用 <code>CancelToken.source</code> 工厂方法创建 <code>cancel token</code>，像这样：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> CancelToken <span class="token operator">=</span> axios<span class="token punctuation">.</span>CancelToken<span class="token punctuation">;</span>
<span class="token keyword">const</span> source <span class="token operator">=</span> CancelToken<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

axios
  <span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/user/12345&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    cancelToken<span class="token operator">:</span> source<span class="token punctuation">.</span>token<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">thrown</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>axios<span class="token punctuation">.</span><span class="token function">isCancel</span><span class="token punctuation">(</span>thrown<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;Request canceled&quot;</span><span class="token punctuation">,</span> thrown<span class="token punctuation">.</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token comment">// 处理错误</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

axios<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>
  <span class="token string">&quot;/user/12345&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;new name&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span>
    cancelToken<span class="token operator">:</span> source<span class="token punctuation">.</span>token<span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 取消请求（message 参数是可选的）</span>
source<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token string">&quot;Operation canceled by the user.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>还可以通过传递一个 <code>executor</code> 函数到 <code>CancelToken</code> 的构造函数来创建 <code>cancel token</code>：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> CancelToken <span class="token operator">=</span> axios<span class="token punctuation">.</span>CancelToken<span class="token punctuation">;</span>
<span class="token keyword">let</span> cancel<span class="token punctuation">;</span>

axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/user/12345&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  cancelToken<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">CancelToken</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">executor</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// executor 函数接收一个 cancel 函数作为参数</span>
    cancel <span class="token operator">=</span> c<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// cancel the request</span>
<span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>注意: 可以使用同一个 <code>cancel token</code> 取消多个请求。</p><h4 id="取消请求实现"><a class="header-anchor" href="#取消请求实现" aria-hidden="true">#</a> 取消请求实现</h4><p>仔细看看它的使用方式：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> CancelToken <span class="token operator">=</span> axios<span class="token punctuation">.</span>CancelToken<span class="token punctuation">;</span>
<span class="token keyword">const</span> source <span class="token operator">=</span> CancelToken<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

axios<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>
  <span class="token string">&quot;/user/12345&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span>
    name<span class="token operator">:</span> <span class="token string">&quot;new name&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span>
    cancelToken<span class="token operator">:</span> source<span class="token punctuation">.</span>token<span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

source<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token string">&quot;Operation canceled by the user.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>执行 <code>source.cancel(&#39;...&#39;)</code> 时，就可以取消请求。实际实现取消请求可以通过如下代码：</p><div class="language-javascript"><pre><code><span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
request<span class="token punctuation">.</span><span class="token function">abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>也就是 <code>xhr.js</code> 中通过执行 <code>abort</code> 方法取消请求，但是 <code>request</code> 对象并没有把它暴露出来。我们该如何实现联动呢？</p><p>通过阅读源码我们知道，通过 <code>promise</code> 来实现的联动。也就是说当执行 <code>source.cancel(&#39;...&#39;)</code> 时，实际上是去改变 <code>promise</code> 的状态。</p><p>在 <code>xhr.js</code> 中只要去监听该 <code>promise</code> 的状态即可：</p><div class="language-javascript"><pre><code><span class="token keyword">if</span> <span class="token punctuation">(</span>cancelToken<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  cancelToken<span class="token punctuation">.</span>promise
    <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">reason</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      request<span class="token punctuation">.</span><span class="token function">abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">reject</span><span class="token punctuation">(</span>reason<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>现在来实现它的核心类 <code>src/cancel/CancelToken.js</code></p><div class="language-javascript"><pre><code><span class="token keyword">class</span> <span class="token class-name">Cancel</span> <span class="token punctuation">{</span>
  message<span class="token punctuation">;</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> message<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 判断抛出的错误是不是 Cancel 实例</span>
<span class="token keyword">function</span> <span class="token function">isCancel</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> value <span class="token keyword">instanceof</span> <span class="token class-name">Cancel</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">CancelToken</span> <span class="token punctuation">{</span>
  promise<span class="token punctuation">;</span> <span class="token comment">// 定义 promise 变量，用来存储状态</span>
  reason<span class="token punctuation">;</span> <span class="token comment">// 定义错误原因变量</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">executor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 定义一个空变量，用它来存储一个 promise 实例的 resolve 方法</span>
    <span class="token keyword">let</span> resolvePromise<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>promise <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      resolvePromise <span class="token operator">=</span> resolve<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">const</span> <span class="token function-variable function">paramFn</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">message</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>reason<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>reason <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cancel</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">resolvePromise</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>reason<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token comment">// 执行实例化时传入的方法，并使用 paramFn 作为参数传入</span>
    <span class="token function">executor</span><span class="token punctuation">(</span>paramFn<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">throwIfRequested</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>reason<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">this</span><span class="token punctuation">.</span>reason<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 定义 source 静态方法，导出 CancelToken 实例以及取消方法 cancel</span>
  <span class="token keyword">static</span> <span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> cancel<span class="token punctuation">;</span>
    <span class="token keyword">const</span> token <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CancelToken</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">c</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      cancel <span class="token operator">=</span> c<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      cancel<span class="token punctuation">,</span>
      token<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>最后在 <code>axios.js</code> 中添加相应的方法：</p><div class="language-javascript"><pre><code>axios<span class="token punctuation">.</span>CancelToken <span class="token operator">=</span> CancelToken<span class="token punctuation">;</span>
axios<span class="token punctuation">.</span>Cancel <span class="token operator">=</span> Cancel<span class="token punctuation">;</span>
axios<span class="token punctuation">.</span>isCancel <span class="token operator">=</span> isCancel<span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><p>最后我们来分析下调用过程：</p><p>1、执行 <code>source.cancel(&#39;...&#39;);</code> 相当于执行 <code>paramFn</code> 方法。</p><p>2、执行 <code>paramFn</code> 方法时，通过 <code>resolvePromise(this.reason)</code> 改变 <code>this.promise</code> 为 <code>resolve</code> 状态。</p><p>3、在 <code>xhr.js</code> 中，通过 <code>cancelToken.promise.then(reason =&gt; {request.abort()})</code>，监听了 <code>promise</code> 的状态，当发生改变时，就对该请求进行取消。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F11_%E5%8F%96%E6%B6%88%E8%AF%B7%E6%B1%82" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="withcredentials"><a class="header-anchor" href="#withcredentials" aria-hidden="true">#</a> withCredentials</h3><p>在同域情况下，发送请求会默认携带当前域下的 <code>cookie</code>，但是跨域的情况下，默认是不会携带请求域下的 <code>cookie</code>，如果想携带，只需要设置请求的 <code>xhr</code> 对象的 <code>withCredentials</code> 为 <code>true</code> 即可。</p><p>这个实现起来非常简单，只需要在 <code>xhr.js</code> 中添加这么一段代码：</p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token function">xhrAdapter</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> <span class="token punctuation">{</span>
      <span class="token operator">...</span>
      withCredentials
    <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>

    <span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>

    <span class="token comment">// 设置 xhr 对象的 withCredentials 属性即可</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>withCredentials<span class="token punctuation">)</span><span class="token punctuation">{</span>
      request<span class="token punctuation">.</span>withCredentials <span class="token operator">=</span> withCredentials<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token operator">...</span>
    request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

复制代码
</code></pre></div><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F12_withCredentials" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><h3 id="csrf-防御"><a class="header-anchor" href="#csrf-防御" aria-hidden="true">#</a> CSRF 防御</h3><p><strong>跨站请求伪造</strong>（<code>Cross-site request forgery</code>），通常缩写为 <strong>CSRF</strong> 或者 <strong>XSRF</strong>， 是一种挟制用户在当前已登录的 <code>Web</code> 应用程序上执行非本意的操作的攻击方法。</p><p>一个典型的 <code>CSRF</code> 攻击有着如下的流程：</p><ol><li>受害者登录 <code>a.com</code>，并保留了登录凭证（<code>Cookie</code>）；</li><li>攻击者引诱受害者访问了 <code>b.com</code>；</li><li><code>b.com</code> 向 <code>a.com</code> 发送了一个请求：<code>a.com/act=xx</code>。浏览器会默认携带 <code>a.com</code> 的<code>Cookie</code>；</li><li><code>a.com</code> 接收到请求后，对请求进行验证，并确认是受害者的凭证，误以为是受害者自己发送的请求；</li><li><code>a.com</code> 以受害者的名义执行了 <code>act=xx</code>；</li><li>攻击完成，攻击者在受害者不知情的情况下，冒充受害者，让 <code>a.com</code> 执行了自己定义的操作。</li></ol><p>如果对于 <code>CSRF</code> 防御不熟悉的同学也可以阅读作者的另外一篇文章：<a href="https://juejin.cn/post/6844904158697357319" target="_blank" rel="noopener noreferrer">前端安全（同源策略、XSS 攻击、CSRF 攻击）</a></p><h4 id="axios-是如何防御-csrf"><a class="header-anchor" href="#axios-是如何防御-csrf" aria-hidden="true">#</a> axios 是如何防御 CSRF</h4><p>采用验证 <code>Token</code> 的方式来防御 <code>CSRF</code> 攻击，它的防御过程如下：</p><ol><li>在浏览器向服务器发起请求时，服务器生成一个 <code>CSRF Token</code>。并通过 <code>set-cookie</code> 的方式种到客户端。</li><li>客户端发送请求时，从 <code>cookie</code> 对应字段中读取出 <code>token</code>，然后添加到请求 <code>headers</code> 中。</li><li>服务器解析请求 <code>headers</code>，并验证 <code>token</code>。</li></ol><p>由于这个 <code>token</code> 是很难伪造的，所以就能区分这个请求是否是用户正常发起的。</p><p>因此我们的 <code>axios</code> 库要自动把这几件事情做了，每次发送请求的时候，从 <code>cookie</code> 中读取对应的 <code>token</code> 值，然后添加到请求 <code>headers</code> 中，我们允许用户配置 <code>xsrfCookieName</code> 和 <code>xsrfHeaderName</code>，其中 <code>xsrfCookieName</code> 表示存储 <code>token</code> 的 <code>cookie</code> 名称，<code>xsrfHeaderName</code> 表示请求 <code>headers</code> 中 <code>token</code> 对应的 <code>header</code> 名称。</p><div class="language-javascript"><pre><code><span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  url<span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span>
  xsrfCookieName<span class="token operator">:</span> <span class="token string">&quot;XSRF-TOKEN&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 默认配置</span>
  xsrfHeaderName<span class="token operator">:</span> <span class="token string">&quot;X-XSRF-TOKEN&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 默认配置</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码<span class="token punctuation">;</span>
</code></pre></div><h4 id="axios-实现-csrf-防御"><a class="header-anchor" href="#axios-实现-csrf-防御" aria-hidden="true">#</a> axios 实现 CSRF 防御</h4><p>它的核心实现是在 <code>xhr.js</code> 中：</p><div class="language-javascript"><pre><code>module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token function">xhrAdapter</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> <span class="token punctuation">{</span>
      <span class="token operator">...</span>
      withCredentials<span class="token punctuation">,</span>
      xsrfCookieName<span class="token punctuation">,</span>
      xsrfHeaderName
    <span class="token punctuation">}</span> <span class="token operator">=</span> config<span class="token punctuation">;</span>

    <span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>

    <span class="token comment">// 设置 xhr 对象的 withCredentials 属性即可</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>withCredentials<span class="token punctuation">)</span><span class="token punctuation">{</span>
      request<span class="token punctuation">.</span>withCredentials <span class="token operator">=</span> withCredentials<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 判断如果是配置 withCredentials 为 true，或者是同域请求并且 xsrfCookieName 存在</span>
    <span class="token comment">// 才会在请求 headers 添加 xsrf 相关字段。</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span>withCredentials <span class="token operator">||</span> <span class="token function">isURLSameOrigin</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> xsrfCookieName <span class="token punctuation">)</span><span class="token punctuation">{</span>
      <span class="token comment">// 通过cookie 去读取对应的 xsrfHeaderName 值</span>
      <span class="token keyword">const</span> xsrfValue <span class="token operator">=</span> cookie<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>xsrfCookieName<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span><span class="token punctuation">(</span>xsrfValue <span class="token operator">&amp;&amp;</span> xsrfHeaderName<span class="token punctuation">)</span><span class="token punctuation">{</span>
        headers<span class="token punctuation">[</span>xsrfHeaderName<span class="token punctuation">]</span> <span class="token operator">=</span> xsrfValue<span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token operator">...</span>
    request<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

复制代码
</code></pre></div><p>关于 <code>CSRF</code> 防御的实现并不复杂，但是通过实现它，可以让我们彻底掌握 <code>CSRF</code> 从产生原因到如何防御。</p><p><a href="https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fshiyou00%2Flion-axios%2Ftree%2F13_csrf" target="_blank" rel="noopener noreferrer">点击查看本小结完整代码</a></p><p>到此为止，关于手写 <code>axios</code> 的核心已经完成了，基本上涵盖了 <code>axios</code> 重要的知识点。相信您和作者一样可以收获良多，接下来我们把它打包发到 <code>npm</code> 线上把。</p><h1 id="axios-打包发布"><a class="header-anchor" href="#axios-打包发布" aria-hidden="true">#</a> axios 打包发布</h1><p>1、由于之前我们已经写好了 <code>webpack</code> 配置，因此只需要直接执行打包命令 <code>npm run build</code> 。 2、<code>package.json</code> 优化</p><div class="language-javascript"><pre><code><span class="token punctuation">{</span>
  <span class="token string">&quot;name&quot;</span><span class="token operator">:</span> <span class="token string">&quot;lion-axios&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 包名称</span>
  <span class="token string">&quot;version&quot;</span><span class="token operator">:</span> <span class="token string">&quot;1.0.0&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 版本</span>
  <span class="token string">&quot;description&quot;</span><span class="token operator">:</span> <span class="token string">&quot;lion-axios&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 描述</span>
  <span class="token string">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string">&quot;clean&quot;</span><span class="token operator">:</span> <span class="token string">&quot;rimraf ./dist&quot;</span><span class="token punctuation">,</span>
    <span class="token string">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;webpack-dev-server --env.development --config webpack.common.js&quot;</span><span class="token punctuation">,</span>
    <span class="token string">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;npm run clean &amp;&amp; webpack --env.production --config webpack.common.js&quot;</span><span class="token punctuation">,</span>
    <span class="token string">&quot;prepublishOnly&quot;</span><span class="token operator">:</span> <span class="token string">&quot;npm run build&quot;</span> <span class="token comment">// 执行 npm publish 之前会自动执行打包</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">&quot;repository&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">// 代码托管信息</span>
    <span class="token string">&quot;type&quot;</span><span class="token operator">:</span> <span class="token string">&quot;git&quot;</span><span class="token punctuation">,</span>
    <span class="token string">&quot;url&quot;</span><span class="token operator">:</span> <span class="token string">&quot;git+https://github.com/shiyou00/lion-axios.git&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">&quot;files&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token string">&quot;dist&quot;</span> <span class="token comment">// 项目上传至npm服务器的文件，可以是单独的文件、整个文件夹，或者通配符匹配到的文件。</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token string">&quot;main&quot;</span><span class="token operator">:</span> <span class="token string">&quot;dist/axios.js&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 入口文件</span>
  <span class="token string">&quot;keywords&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">&quot;lion-axios&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// 关键词</span>
  <span class="token string">&quot;author&quot;</span><span class="token operator">:</span> <span class="token string">&quot;Lion&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 作者</span>
  <span class="token string">&quot;license&quot;</span><span class="token operator">:</span> <span class="token string">&quot;ISC&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 协议</span>
  <span class="token string">&quot;bugs&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string">&quot;url&quot;</span><span class="token operator">:</span> <span class="token string">&quot;https://github.com/shiyou00/lion-axios/issues&quot;</span> <span class="token comment">// 提 bug 地址</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">&quot;homepage&quot;</span><span class="token operator">:</span> <span class="token string">&quot;https://github.com/shiyou00/lion-axios#readme&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 主页地址</span>
<span class="token punctuation">}</span>
复制代码
</code></pre></div><p>3、登录注册 <code>npm</code></p><div class="language-javascript"><pre><code># 查看是否登录
npm whoami

# 注册
npm adduser
Username<span class="token operator">:</span>shiyou
Email<span class="token operator">:</span><span class="token punctuation">(</span>xxx@xxx<span class="token punctuation">.</span>com<span class="token punctuation">)</span>

# 登录
npm login
然后填写注册信息即可
复制代码
</code></pre></div><p>4、执行 <code>npm publish</code> 发布</p><p>查看 <a href="https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Flion-axios" target="_blank" rel="noopener noreferrer">lion-axios 包线上地址</a></p><p>5、使用 <code>lion-axios</code></p><div class="language-javascript"><pre><code># 安装
yarn add lion<span class="token operator">-</span>axios 或 npm install lion<span class="token operator">-</span>axios <span class="token operator">--</span>save

# 引入
<span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">&#39;lion-axios&#39;</span><span class="token punctuation">;</span>

# 使用
<span class="token keyword">const</span> baseURL <span class="token operator">=</span> <span class="token string">&quot;https://reqres.in/api/users&quot;</span><span class="token punctuation">;</span>
<span class="token function">axios</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  method<span class="token operator">:</span> <span class="token string">&quot;get&quot;</span><span class="token punctuation">,</span>
  url<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>baseURL<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">?foo=bar</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
  params<span class="token operator">:</span> <span class="token punctuation">{</span>
    bar<span class="token operator">:</span> <span class="token string">&quot;baz1&quot;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
复制代码
</code></pre></div><h3 id="总结"><a class="header-anchor" href="#总结" aria-hidden="true">#</a> 总结</h3><ol><li>通过 <code>axios</code> 简介，使我们对 <code>axios</code> 有一个初步的理解，这里还是建议大家去过一遍官方文档。</li><li>通过搭建 <code>axios</code> 项目工程，可以学习到如何搭建一个企业级库的工程项目。</li><li>通过一步一步实现 <code>axios</code> 源码，可以学习到很多精妙的编程思想，这也是实现一个开源库的重要目的。</li><li>最后我们把实现好的库，打包发布到 <code>npm</code> 线上，完成了一个开源库的完整生命周期。</li></ol><p>如果公司需要你手动搭建一个开源库，是不是已经胸有成竹了</p><h2 id="宝藏网站"><a class="header-anchor" href="#宝藏网站" aria-hidden="true">#</a> 宝藏网站</h2><p><a href="https://www.30secondsofcode.org/css/p/1" target="_blank" rel="noopener noreferrer">https://www.30secondsofcode.org/css/p/1</a> 30s 代码片段</p><p><a href="https://1loc.dev/" target="_blank" rel="noopener noreferrer">https://1loc.dev/</a> 很多的 js 方法</p></div></div><footer class="page-footer" data-v-7eddb2c4 data-v-fb8d84c6><div class="edit" data-v-fb8d84c6><div class="edit-link" data-v-fb8d84c6 data-v-1ed99556><!----></div></div><div class="updated" data-v-fb8d84c6><!----></div></footer><div class="next-and-prev-link" data-v-7eddb2c4 data-v-38ede35f><div class="container" data-v-38ede35f><div class="prev" data-v-38ede35f><a class="link" href="/interview/npm/" data-v-38ede35f><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="icon icon-prev" data-v-38ede35f><path d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"></path></svg><span class="text" data-v-38ede35f>包管理工具</span></a></div><div class="next" data-v-38ede35f><a class="link" href="/interview/solution/" data-v-38ede35f><span class="text" data-v-38ede35f>一些解决方案</span><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="icon icon-next" data-v-38ede35f><path d="M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"></path></svg></a></div></div></div><!--[--><!--]--></div></main></div><!----><!--]--></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"index.md\":\"992e2bc4\",\"css_advanced.md\":\"157298f3\",\"css_index.md\":\"0df1ef28\",\"browser_advanced.md\":\"a7a79457\",\"browser_index.md\":\"9938c247\",\"browser_plugin.md\":\"fd27046d\",\"git_index.md\":\"fb33a92a\",\"html_advanced.md\":\"599ad3d5\",\"html_index.md\":\"c94736b6\",\"interview_index.md\":\"07dc8acc\",\"interview_two.md\":\"0ca812f6\",\"javascript_advanced.md\":\"5c434e6d\",\"javascript_index.md\":\"d996a3fc\",\"jenkins_index.md\":\"3068e232\",\"myself_index.md\":\"340fc837\",\"myself_one.md\":\"31202d59\",\"myself_pre.md\":\"aa93248e\",\"network_index.md\":\"7251e8b9\",\"network_security.md\":\"d11dae3c\",\"node_index.md\":\"e0b22fc7\",\"node_pit.md\":\"a1502c64\",\"npm_index.md\":\"59b033b6\",\"pit_index.md\":\"603d46aa\",\"solution_index.md\":\"987aa705\",\"typescript_advanced.md\":\"431b8bba\",\"typescript_combat.md\":\"60c9a8a2\",\"typescript_index.md\":\"bd88b5a2\",\"vite_index.md\":\"d6838c03\",\"vite_optimize.md\":\"49f1bb24\",\"vue_index.md\":\"380ec221\",\"vue_reactive.md\":\"206c8e46\",\"vue_router.md\":\"0d556c25\",\"vue_source.md\":\"592e192a\",\"vue_vuex.md\":\"032502ef\",\"webpack_advanced.md\":\"ae6c63b7\",\"webpack_common.md\":\"141deb7a\",\"webpack_custom.md\":\"4827ebf8\",\"webpack_index.md\":\"49f185a6\",\"webpack_optimize.md\":\"545698f9\",\"vue_source_index.md\":\"1c12bc29\",\"vue_source_nexttick.md\":\"4533a28b\",\"vue_source_watch.md\":\"0e7342fc\"}")</script>
    <script type="module" async src="/interview/assets/app.49accf37.js"></script>
  </body>
</html>